package MetaInit

import (
	"db2s/Er"
	"db2s/arg"
	"db2s/global"
	"strings"
)

func getPartitionMeta(i pcgInitS) (res global.PartMetaResultS, err error) {
	var (
		t Er.TablesMetaInfoEr
	)
	if t, err = Er.MetaInfo(Er.TableInfoMeta{
		DBType: i.dbName,
	}); err != nil {
		return
	}
	if res, err = t.GetPartMeta(pcgInit(i)); err != nil {
		return
	}
	return
}
func (t TableMeta) getAllPartMeta(i arg.InputTableAttributes) (res []global.PartMetaResultS, err error) {
	var (
		meta global.PartMetaResultS
	)
	for _, v := range []pcgInitS{
		{logSeq: t.LogSeq, dbName: t.DSns.SrcDBName, db: t.SDB, schema: i.Schema, table: i.Table, input: t.Input},
		{logSeq: t.LogSeq, dbName: t.DSns.DestDBName, db: t.DDB, schema: i.MSchema, table: i.MTable, input: t.Input},
	} {
		if meta, err = getPartitionMeta(v); err != nil {
			return
		}
		res = append(res, meta)
	}
	return
}
func partRightFilter(filter, meta string) (res string) {
	var key string
	if n := strings.Index(strings.ToLower(filter), "%"); n != -1 {
		key = strings.ToLower(filter)[n+1:]
	} else {
		key = strings.ToLower(filter)
	}
	if strings.HasSuffix(strings.ToLower(meta), strings.ToLower(key)) { //支持模糊匹配
		res = meta
	}
	return
}
func partLeftFilter(filter, meta string) (res string) {
	var key string
	if n := strings.Index(strings.ToLower(filter), "%"); n != -1 {
		key = strings.ToLower(filter)[:n]
	} else {
		key = strings.ToLower(filter)
	}
	if strings.HasPrefix(strings.ToLower(meta), strings.ToLower(key)) { //支持模糊匹配
		res = meta
	}
	return
}
func partMiddleFilter(filter, meta string) (res string) {
	var key1, key2 string
	if n := strings.Index(strings.ToLower(filter), "%"); n != -1 {
		key1 = strings.ToLower(filter)[:n]
		key2 = strings.ToLower(filter)[n+1:]
	} else {
		key1 = strings.ToLower(filter)
		key2 = strings.ToLower(filter)
	}
	if strings.HasPrefix(strings.ToLower(meta), key1) && strings.HasSuffix(strings.ToLower(meta), key2) {
		res = meta
	}
	return
}
func partLeftRightFilter(filter, meta string) (res string) {
	var key = strings.ReplaceAll(strings.ToLower(filter), "%", "")
	if strings.Contains(strings.ToLower(filter), key) {
		res = meta
	}
	return
}
func specifyPartitionFilter(filter []string, meta []string) (res []string) {
	res = meta
	if len(filter) == 0 || len(meta) == 0 {
		return
	}
	res = []string{}
	for _, k := range filter {
		for _, v := range meta {
			if strings.HasPrefix(k, "%") && !strings.Contains(strings.TrimPrefix(k, "%"), "%") { //右匹配
				if r := partRightFilter(k, v); len(r) > 0 {
					res = append(res, r)
				}
			} else if strings.HasSuffix(k, "%") && !strings.Contains(strings.TrimSuffix(k, "%"), "%") {
				if r := partLeftFilter(k, v); len(r) > 0 {
					res = append(res, r)
				}
			} else if strings.Contains(k, "%") && !strings.HasPrefix(k, "%") && !strings.HasSuffix(k, "%") { //支持模糊匹配
				if r := partMiddleFilter(k, v); len(r) > 0 {
					res = append(res, r)
				}
			} else if strings.HasPrefix(k, "%") && strings.HasSuffix(k, "%") {
				if r := partLeftRightFilter(k, v); len(r) > 0 {
					res = append(res, r)
				}
			} else {
				if strings.EqualFold(v, k) {
					res = append(res, v)
				}
			}
		}
	}
	return
}
func specifyIgnorePartitionFilter(filter, meta []string) (res []string) {
	res = meta
	if len(filter) == 0 || len(meta) == 0 {
		return
	}
	res = []string{}
	for _, x := range filter {
		for _, y := range meta {
			if !strings.HasPrefix(strings.ToLower(y), strings.ToLower(x)) { //支持模糊匹配
				res = append(res, y)
			}
		}
	}
	return
}

// 筛选指定分区名、原目标端分区名合并、忽略分区名
func partNameMerge(i arg.InputTableAttributes, metaGarth []global.PartMetaResultS) (res []string) {
	var (
		spg = i.TableAttributes.PartitionGather.PartitionGather
		sig = i.TableAttributes.PartitionGather.IgnorePartitionGather
	)
	pg := specifyIgnorePartitionFilter(sig, specifyPartitionFilter(spg, metaGarth[0].NameGarth))
	for _, x := range pg {
		var exist bool
		for _, y := range metaGarth[1].NameGarth {
			if strings.EqualFold(x, y) {
				exist = true
				break
			}
		}
		if exist {
			res = append(res, x)
		}
	}
	return
}
func partShardFilter(pg []string, meta []global.PartShardS) (res []global.PartShardS) {
	for _, v := range pg {
		for _, x := range meta {
			if strings.EqualFold(v, x.PartName) {
				res = append(res, x)
			}
		}
	}
	return
}

func partMetaMerge1(pg []string, metaGarth []global.PartMetaResultS) (src, dst []global.PartShardS) {
	return partShardFilter(pg, metaGarth[0].Shard), partShardFilter(pg, metaGarth[1].Shard)
}

// partIndexCategoryChooseNormal 分区表根据选中的索引的Category来选择是走分区还是走单表
func partIndexCategoryChooseNormal(IndexCategory string) bool {
	switch strings.ToLower(IndexCategory) {
	case "local":
		return false
	case "global":
		return true
	default:
		return false
	}
}
func partToNormal(tableName string) (res global.PartMetaResultS) {
	return global.PartMetaResultS{
		Shard: []global.PartShardS{
			{
				Mode:             "single",
				PartName:         "single",
				ShardName:        "single",
				BackendTableName: tableName,
			},
		},
		NameGarth: []string{"single"},
	}
}
func dstNormalToPart(metaGarth []global.PartMetaResultS) (res global.PartMetaResultS) {
	var normalBase = metaGarth[1].Shard[0]
	for _, v := range metaGarth[0].NameGarth {
		res.Shard = append(res.Shard, global.PartShardS{
			Mode:             normalBase.Mode,
			PartName:         v,
			BackendTableName: normalBase.BackendTableName,
			ShardName:        normalBase.ShardName,
			SubPartName:      normalBase.SubPartName,
		})
	}
	res.NameGarth = metaGarth[0].NameGarth
	return
}

// 源目标端表类型继续分类检查
func partMetaMerge(forcePart, indexCategory, tableName string, metaGarth []global.PartMetaResultS) (res []global.PartMetaResultS) {
	if len(forcePart) > 0 && strings.EqualFold(forcePart, "normal") { //强制走single表
		res = append(res, partToNormal(tableName))
		res = append(res, partToNormal(tableName))
	}
	if !metaGarth[0].Overview.Status { //源端normal
		if metaGarth[1].Overview.Status { // 目标端partition  //不支持
			res = append(res, metaGarth[0])
			res = append(res, partToNormal(tableName))
			return
		}
		return metaGarth
	}
	if !metaGarth[1].Overview.Status { //源端为partition，目标端为normal
		if partIndexCategoryChooseNormal(indexCategory) { //源端的索引分布是全局的
			res = append(res, partToNormal(tableName))
			res = append(res, metaGarth[1])
			return
		}
		//源端索引分布为本地
		res = append(res, metaGarth[0])
		res = append(res, dstNormalToPart(metaGarth))
		return
	}
	if !metaGarth[0].Overview.ShardModeStatus { //源端为partition，目标端为partition，源端shard分布为normal
		if partIndexCategoryChooseNormal(indexCategory) { //源端的索引分布是全局的
			res = append(res, partToNormal(tableName))
			res = append(res, partToNormal(tableName))
			return
		}
		//开始比较分区列名，分区算法、分区列的字符集是否匹配
		//return metaGarth
	}
	res = metaGarth
	return
}
func (t TableMeta) PartitionInit(q ObjectMetaInput) (res global.PartMetaMarge, err error) {
	var (
		metaGarth []global.PartMetaResultS
	)
	if len(q.TableAttributes.TableAttributes.ForcePart) > 0 && strings.EqualFold(q.TableAttributes.TableAttributes.ForcePart, "normal") {
		return global.PartMetaMarge{
			Name:         []string{"single"},
			Logo:         "single",
			SrcPartShard: partToNormal(q.TableAttributes.Table).Shard,
			DstPartShard: partToNormal(q.TableAttributes.MTable).Shard,
		}, err
	}
	if metaGarth, err = t.getAllPartMeta(q.TableAttributes); err != nil || len(metaGarth) < 2 {
		return
	}
	metaGarth = partMetaMerge(q.TableAttributes.TableAttributes.ForcePart, q.TIndex.IndexCategory, q.TableAttributes.MTable, metaGarth)
	res.Name = partNameMerge(q.TableAttributes, metaGarth)
	res.SrcPartShard, res.DstPartShard = partMetaMerge1(res.Name, metaGarth)
	return
}

//// TablePartitionMerge 分区表合并
//func (t TableMeta) TablePartitionMerge(stp []global.TablePartitionConfig, schema, table string) (partitionMerge TablePartitionMerge) {
//	var (
//		event                             = "[TablePartitionMerge]"
//		specifyPartition, ignorePartition = make(map[string]int), make(map[string]int)
//	)
//	WriteLog.Info(fmt.Sprintf("(%v) %v Start merging the partition data of table %v.%v and filter the specified partition", t.LogSeq, event, schema, table))
//	partitionMerge.Schema = schema
//	partitionMerge.Table = table
//	partitionMerge.PartitionBackendTableName = make(map[string]string)
//	partitionMerge.PartitionNameShardName = make(map[string]string)
//	if p1, ok := t.Schema.Partition[t.getSchemaTableMerge(schema, table)]; ok {
//		for _, vv := range p1.PartitionGather {
//			specifyPartition[strings.ToUpper(vv)]++
//		}
//		for _, vv := range p1.IgnorePartitionGather {
//			ignorePartition[strings.ToUpper(vv)]++
//		}
//	}
//	if len(specifyPartition) > 0 {
//		WriteLog.Info(fmt.Sprintf("(%v) %v Table %v.%v specifies partitions, and the current partition set is %v", t.LogSeq, event, schema, table, specifyPartition))
//	}
//	if len(ignorePartition) > 0 {
//		WriteLog.Info(fmt.Sprintf("(%v) %v Table %v.%v ignore partitions, and the current partition set is %v", t.LogSeq, event, schema, table, ignorePartition))
//	}
//	for _, v := range stp {
//		partitionMerge.tableObject = v.TableObject
//		if strings.EqualFold(v.TableObject, "single") {
//			partitionMerge.PartitionName = append(partitionMerge.PartitionName, v.FirstPartition.Name)
//			partitionMerge.PartitionNameShardName = map[string]string{v.FirstPartition.Name: v.ShardName}
//			WriteLog.Info(fmt.Sprintf("(%v) %v The partition type of table %v.%v is single, exit the merge", t.LogSeq, event, schema, table))
//			return
//		}
//		_, pg := specifyPartition[strings.ToUpper(v.FirstPartition.Name)]
//		_, ig := ignorePartition[strings.ToUpper(v.FirstPartition.Name)]
//		if (len(specifyPartition) > 0 && !pg) || (len(ignorePartition) > 0 && ig) {
//			WriteLog.Info(fmt.Sprintf("(%v) %v The partition name %v of table %v.%v does not match the specified partition or matches the ignored partition, and the merge is skipped.", t.LogSeq, event, v.FirstPartition.Name, schema, table))
//			continue
//		}
//		//if len(v.SubPartMeta) > 0 { //不支持二级分区，因为hash分区可能会导致原目标端的二级分区名不一致
//		//	partitionMerge.PartitionName = append(partitionMerge.PartitionName, "single")
//		//	partitionMerge.PartitionNameShardName = map[string]string{v.FirstPartition.Name: "single"}
//		//	return
//		//}
//		partitionName := v.FirstPartition.Name
//		partitionMerge.PartitionMeth = fmt.Sprintf("%v_%v", v.FirstPartition.Meth, v.SubPartMeth)
//		partitionMerge.PartitionName = append(partitionMerge.PartitionName, partitionName)
//		partitionMerge.PartitionBackendTableName[partitionName] = v.BackendTableName
//		partitionMerge.PartitionNameShardName[partitionName] = v.ShardName
//	}
//	return
//}

//// GetTablePartition 获取表的分区信息
//func (t TableMeta) GetTablePartition(i *inputArg.InputTableAttributes, tIndex IndexChooseResult) (f3 global.PartitionGarth, err error) {
//	var (
//		event = "[GetTablePartition]"
//	)
//	var (
//		sdPartitionNameMap = make(map[string]string)
//		object             = "single"
//		SpecifyPartition   []string
//	)
//	if strings.EqualFold(s3.PartMeth, s4.PartMeth) && !strings.EqualFold(s3.PartMeth, "single") { //两个都是分区表
//		columnType := strings.EqualFold(strings.Join(s3.PartColumn.FirstPar.Type, ","), strings.Join(s4.PartColumn.FirstPar.Type, ","))
//		columnName := strings.EqualFold(strings.Join(s3.PartColumn.FirstPar.Name, ","), strings.Join(s4.PartColumn.FirstPar.Name, ","))
//		columnCharset := strings.EqualFold(strings.Join(s3.PartColumn.FirstPar.CharSet, ","), strings.Join(s4.PartColumn.FirstPar.CharSet, ","))
//		columnCollation := strings.EqualFold(strings.Join(s3.PartColumn.FirstPar.Collation, ","), strings.Join(s4.PartColumn.FirstPar.Collation, ","))
//		indexCategory := strings.EqualFold(tIndex.IndexCategory, "local")
//		if columnType && columnName && columnCharset && columnCollation && indexCategory {
//			object = "partition"
//			for _, k := range SpecifyPartition {
//				for _, v := range s4.PartitionNameGarth {
//					if strings.EqualFold(k, v) {
//						sdPartitionNameMap[k] = v
//					}
//				}
//			}
//		} else {
//			object = "single"
//			SpecifyPartition = []string{"single"}
//			s3.PartMeth = "single"
//			s4.PartMeth = "single"
//			s3.PartitionBackendShard = map[string]global.BackendTable{"single": {
//				ShardName:        "single",
//				BackendTableName: i.Table,
//			},
//			}
//			s4.PartitionBackendShard = map[string]global.BackendTable{"single": {
//				ShardName:        "single",
//				BackendTableName: i.MTable,
//			},
//			}
//		}
//	}
//	if !strings.EqualFold(s3.PartMeth, "single") && strings.EqualFold(s4.PartMeth, "single") { //源端是分区表，目标端是普通表
//		if strings.EqualFold(tIndex.IndexCategory, "local") {
//			for _, k := range SpecifyPartition {
//				sdPartitionNameMap[k] = "single"
//			}
//		} else {
//			sdPartitionNameMap["single"] = "single"
//			SpecifyPartition = []string{"single"}
//		}
//	} else if strings.EqualFold(s3.PartMeth, "single") && !strings.EqualFold(s4.PartMeth, "single") { //源端是普通表，目标端是分区表
//		//for _, k := range SpecifyPartition {
//		sdPartitionNameMap["single"] = "single"
//		//}
//	} else { //都是普通表
//		sdPartitionNameMap["single"] = "single"
//	}
//
//	f3 = global.PartitionGarth{
//		Logo: global.PartitionGarthLogo{
//			Source: func() string {
//				if !strings.EqualFold(s3.PartMeth, "single") {
//					return "partition"
//				}
//				return "single"
//			}(),
//			Dest: func() string {
//				if !strings.EqualFold(s4.PartMeth, "single") {
//					return "partition"
//				}
//				return "single"
//			}(),
//		},
//		Name: global.PartitionGarthName{
//			Source: SpecifyPartition,
//			Dest:   s4.PartitionNameGarth,
//		},
//		BackendTableName: global.PartitionGarthBackendTableName{
//			Source: s3.PartitionBackendShard,
//			Dest:   s4.PartitionBackendShard,
//		},
//		Object:            object,
//		SourceDestNameMap: sdPartitionNameMap,
//	}
//	return
//}
